GameplayBuffSystem框架
基础框架
由 BuffManager
,管理所有的 BuffComponent
;
对于每个 Player
,由 BuffComponent
管理该 Player
身上持有的 Buff
;
classDiagram
UGameplayBuffManager..>FGameplayBuffSetting
UGameplayBuffManager..>UGameplayBuffComponent
UGameplayBuffManager..>FGameplayBuffParams
class UGameplayBuffManager {
BuffSettings : TMap~int, FGameplayBuffSetting~
+CommitBuff(Target, Buff, Params)
+QueryBuff(Target, Buff)
+CombineBuffParams(Target, Buff, Params)
+RemoveBuff(Target, Buff, bRemoveImmediately)
+ClearBuff(Target)
-Tick()
-CreateBuff(Target, Buff, Params)
-GetMergedParams(Buff, Params)
-InitBuffSetting()
}
UGameplayBuffComponent..>UGameplayBuffBase
class UGameplayBuffComponent {
BuffGroup : TArray~UGameplayBuffBase*~
BuffIDArray : int (For Sync)
AddBuff(InBuff)
RemoveBuff(InBuff, bRemoveImmediately)
}
class FGameplayBuffSetting {
ID : int
Name : FString
bNeedToMerge : bool
AssetPath : FString
BuffAsset : TSubclassOf~UGameplayBuffBase~
DefaultParams : FGameplayBuffParams
}
class FGameplayBuffParams {
# ValueMap : TMap~FString, FVariant~
# Tags : TArray~FString~
FGameplayBuffParams(std::initializer_list ~TPairInitializer[const FString&, FVariant]~ ValuePairs)
SetValue(const FString& FieldName, FVariant Value)
Contains(const FString& FieldName)
IsEmpty()
Merge(const FGameplayBuffParams& OtherParams)
GetValueMap() const
operator+(const FGameplayBuffParams& OtherParams)
}
class FGameplayCountDownData {
StartTime
TotalTime
LeftTime
IsPause
SpeedFactor
}
UGameplayBuffBase..>FGameplayBuffParams
class UGameplayBuffBase {
Owner : TWeakObjectPtr~UGameBuffComponent~
BuffID : int
BuffName : FString
BuffParams : FGameplayBuffParams
+Create(Owner, BuffID, Params)
+Remove()
+Merge(BuffHandle, Params)
+CombineParams(Params)
+Tick(DeltaTime)
+CheckNeetToStop()
+SetNeedToRemove(bEnable)
#OnCreate()
#OnRemove()
#OnMerge()
#OnTick()
}
UGameplayBuffBase<|--UGameplayBuff_TimeDuration
UGameplayBuff_TimeDuration..>FGameplayCountDownData
class UGameplayBuff_TimeDuration {
# TimeDuration : float
# PassDuration : float
# TickInternal : float
# LastTickTime : float
- SpeedFactor : float
- bInPause : false
- PauseReasons : TArray~FString~
# Tick(DeltaTime)
+ Refresh(InPassDuration, InTimeDuration)
+ BeginPauseTime(Reason)
+ StopPauseTime(Reason)
+ UpdateSpeedFactor(InFactor)
+ GetTimeDuration()
+ GetLeftTime()
+ GetCountDownData()
# OnBeginPauseTime()
# OnStopPauseTime()
# OnUpdateSpeedFactor()
# OnRefresh()
# OnTimeStateChanged()
}
UGameplayBuffUtils..>UGameplayBuffManager
BuffManager
BuffSetting
由 BuffManager
管理 Buff
本身的配置,即 FGameplayBuffSetting
;
在这里通过外部配置的 Buff
表的 Cfg
,读取 AssetPath
并加载对应 BuffClass
,保存外部配置的静态 Tags
、 DefaultParams
(后续可与运行时传入的 Tags
、 Params
进行 Merge
);
同时维护 Tags
、BuffID
、BuffName
之间的映射,方便后续管理;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 void UGameplayBuffManager::InitBuffSettings () { for (auto Cfg : Cfgs) { FGameplayBuffSetting BuffSetting; bool bSuccess = CreateBuffSetting (Cfg, BuffSetting); if (!bSuccess) continue ; ProcessBuffSetting (BuffSetting); } } bool UGameplayBuffManager::CreateBuffSetting (GameplayBuffSettingCfg* Cfg, FGameplayBuffSetting& BuffSetting) { if (Cfg == nullptr ) return false ; int BuffID = Cfg->ID (); FString RowAssetPath = Cfg->AssetPath (); FString AssetPath = GetProcessedAssetPath (RowAssetPath); if (AssetPath.IsEmpty ()) { return false ; } TSubclassOf<UGameplayBuffBase> Asset = LoadClass<UGameplayBuffBase>(this , *AssetPath); if (!IsValid (Asset)) { return false ; } BuffSetting.ID = BuffID; BuffSetting.Name = Cfg->Name (); BuffSetting.bNeedToMerge = Cfg->bNeedToMerge (); BuffSetting.AssetPath = AssetPath; BuffSetting.Asset = Asset; { auto & DefaultParams = BuffSetting.DefaultParams; for (int Index = 0 ; Index < FMath::Min (Cfg->Key_size (), Cfg->Value_size ()); Index++) { FString Key = Cfg->Key (Index); if (!DefaultParams.Contains (Key)) { DefaultParams.SetValue (Key, Cfg->Value (Index) ); } } for (auto Tag : Cfg->Tag ()) { DefaultParams.Tags.AddUnique ( Tag ); } } return true ; } void UGameplayBuffManager::ProcessBuffSetting (const FGameplayBuffSetting& BuffSetting) { int BuffID = BuffSetting.ID; FString BuffName = BuffSetting.Name; if (BuffSettings.Contains ( BuffID )) { return ; } if (BuffNameToIDMap.Contains ( BuffName )) { return ; } BuffSettings.Add ( BuffID, BuffSetting ); BuffNameToIDMap.Add ( BuffName, BuffID ); for (const auto & Tag : BuffSetting.DefaultParams.Tags) { BuffTagToIDsMap.FindOrAdd (Tag).AddUnique ( BuffID ); } }
具体实现
Commit
:判断 BuffSettings
里是否有对应 BuffID
的 Buff
,若存在则创建该实例;
判断一下 BuffComponent
里是否原本已经有相同 BuffID
的 Buff
,若存在,并且该 Buff
需要 Merge
的话,执行 OldBuff
的 Merge
,并将新创建的 Buff
给 Remove
;
否则直接执行 BuffComponent
的 AddBuff
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 UGameplayBuffBase* UGameplayBuffManager::CommitBuff (UGameplayBuffComponent* Target, int BuffID, const FGameplayBuffParams& Params) { if (Target == nullptr ) { return nullptr ; } FGameplayBuffParams MergedParams = GetMergedParams ( BuffID, Params ); UGameplayBuffBase* Buff = CreateBuff (Target, BuffID, MergedParams); if (Buff == nullptr ) { return nullptr ; } auto SpecBuffGroup = Target->GetBuffGroupByBuffID (BuffID); if (SpecBuffGroup.Num () && BuffSettings[BuffID].bNeedToMerge == true ) { UGameplayBuffBase* OldBuff = SpecBuffGroup[0 ]; OldBuff->Merge (Buff, MergedParams); Buff->Remove (); return OldBuff; } else { Target->AddBuff (Buff); return Buff; } } UGameplayBuffBase* UGameplayBuffManager::CreateBuff (UGameplayBuffComponent* Target, int BuffID, const FGameplayBuffParams& Params) { if (!BuffSettings.Contains (BuffID) || !IsValid (BuffSettings[BuffID].Asset)) { return nullptr ; } UGameplayBuffBase* BuffInst = NewObject<UGameplayBuffBase>( Target, BuffSettings[BuffID].Asset ); if (BuffInst == nullptr ) { return nullptr ; } BuffInst->Create (Target, BuffID, Params); return BuffInst; }
Query
:判断BuffComponent
上是否有对应ID的Buff
,返回对应 Handle
:
1 2 3 4 5 6 7 8 9 10 TArray<UGameplayBuffBase*> UGameplayBuffManager::QueryBuff (UGameplayBuffComponent* Target, int BuffID) { if (Target == nullptr ) { return TArray<UGameplayBuffBase*>(); } auto SpecBuffGroup = Target->GetBuffGroupByBuffID (BuffID); return SpecBuffGroup; }
Remove
:通过 BuffID
或者 Handle
把 BuffComponent
上存在的对应 Buff
给 Remove
。
特别的,为了解决 Buff
之间的依赖问题(比如 Buff(A->B)
,在 A、B
的 Remove
都调用到了另一个 Buff
的 Remove
,会导致循环 Remove
问题),维护一个 bRemoveImmediately
(默认为false
),每次调用 Remove
时只是 MarkDirty
(把移除标记设为 true
),在下一次 Tick
才会实际移除。
这样就可以一次 Tick
移除一个 Buff
,通过时间来解开了这个循环依赖的链条。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 void UGameplayBuffManager::RemoveBuff (UGameplayBuffComponent* Target, int BuffID, bool bRemoveImmediately) { if (Target == nullptr ) { return ; } auto SpecBuffGroup = Target->GetBuffGroupByBuffID (BuffID); if (!SpecBuffGroup.Num ()) { return ; } for (int Index = SpecBuffGroup.Num () - 1 ; Index >= 0 ; Index--) { Target->RemoveBuff (SpecBuffGroup[Index], bRemoveImmediately); } } void UGameplayBuffManager::RemoveBuff (UGameplayBuffComponent* Target, UGameplayBuffBase* Buff, bool bRemoveImmediately) { if (Target == nullptr ) { return ; } Target->RemoveBuff (Buff, bRemoveImmediately); } void UGameplayBuffManager::ClearBuff (UGameplayBuffComponent* Target) { if (Target == nullptr ) { return ; } auto BuffGroup = Target->GetBuffGroup (); for (int Index = BuffGroup.Num () - 1 ; Index >= 0 ; Index--) { if (Index >= BuffGroup.Num ()) continue ; auto Buff = BuffGroup[Index]; if (Buff == nullptr ) continue ; Target->RemoveBuff (Buff); } }
Combine
:Buff
显然需要支持传入参数,在 Create
的时候传参,或者通过 CombineParams
将参数传入 Buff
中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 void UGameplayBuffManager::CombineBuffParams (UGameplayBuffComponent* Target, int BuffID, const FGameplayBuffParams& Params) { if (Target == nullptr ) { return ; } auto SpecBuffGroup = Target->GetBuffGroupByBuffID (BuffID); if (!SpecBuffGroup.Num ()) { return ; } for (auto Buff : SpecBuffGroup) { if (IsValid (Buff)) { Buff->CombineParams (Params); } } } void UGameplayBuffManager::CombineBuffParams (UGameplayBuffBase* Buff, const FGameplayBuffParams& Params) { if (Buff == nullptr ) { return ; } Buff->CombineParams (Params); }
Tick
:遍历BuffComponent
,遍历其中的 BuffGroup
;对每一个Buff
执行 Tick
,并且检查是否需要 Stop
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 void UGameplayBuffManager::TickComponent (float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) { if (IsClient (this )) return ; Super::TickComponent (DeltaTime, TickType, ThisTickFunction); auto GS = GetOwner<AGameStateBase>(); auto PlayerArray = GS->GetAllPlayerState (); for (auto Player : PlayerArray) { auto Target = UFunctionLibrary::GetPlayerStateComponent<UGameplayBuffComponent>(Player); if (Target == nullptr ) continue ; const auto BuffGroup = Target->GetBuffGroup (); for (auto Buff : BuffGroup) { if (Buff == nullptr ) continue ; if (!Buff->CheckNeedToStop ()) { Buff->Tick (DeltaTime); } } int TotalCount = BuffGroup.Num (); for (int Index = TotalCount - 1 ; Index >= 0 ; Index--) { auto & Buff = BuffGroup[Index]; if (Buff->CheckNeedToStop () || Buff->CheckNeedToRemove ()) { Target->RemoveBuff (Buff, true ); } } } }
BuffComponent
AddBuff
:将 Buff
添加到 BuffGroup
/ BuffIDArray
,通过 BuffIDArray
做客户端的同步(仅同步 BuffID
到客户端)
RemoveBuff
:移除 Buff
,并执行该 Buff
的 Remove
方法,同步对应信息;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 void UGameplayBuffComponent::AddBuff (UGameplayBuffBase* InBuff) { BuffGroup.Add ( InBuff ); GetBuffIDArray_Mutable ().Add ( InBuff->GetBuffID () ); if (IsStandaloneOrDS (this )) { OnRep_BuffIDArray (); } } void UGameplayBuffComponent::RemoveBuff (UGameplayBuffBase* InBuff, bool bRemoveImmediately) { if (InBuff == nullptr ) return ; int TotalCount = BuffGroup.Num (); for (int Index = TotalCount - 1 ; Index >= 0 ; Index--) { if (BuffGroup[Index] == InBuff) { if (bRemoveImmediately == true ) { BuffGroup.RemoveAt ( Index ); GetBuffIDArray_Mutable ().RemoveAt ( Index ); InBuff->SetNeedToRemove (true ); InBuff->Remove (); if (IsStandaloneOrDS (this )) { OnRep_BuffIDArray (); } } else { InBuff->SetNeedToRemove (true ); } break ; } } } void UGameplayBuffComponent::OnUninit () { Super::OnUninit (); if (!IsStandaloneOrDS (this )) return ; for (int Index = BuffGroup.Num () - 1 ; Index >= 0 ; Index--) { if (Index >= BuffGroup.Num ()) continue ; auto Buff = BuffGroup[Index]; if (Buff == nullptr ) continue ; BuffGroup.RemoveAt ( Index ); Buff->CombineParams ( {"Uninit" , true } ); Buff->Remove (); } } void UGameplayBuffComponent::OnRep_BuffIDArray () { }
Buff
BuffBase
每个 Buff
的实际持有者为 BuffComponent
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 void UGameplayBuffBase::Create (UGameplayBuffComponent* InOwner, int InBuffID, const FGameplayBuffParams& Params) { if (InOwner == nullptr ) return ; Owner = InOwner; BuffID = InBuffID; BuffName = UGameplayBuffUtils::GetBuffName (BuffID); BuffParams = Params; OnCreate (); } void UGameplayBuffBase::Remove () { if (Owner == nullptr ) return ; OnRemove (); Owner = nullptr ; BuffID = 0 ; } void UGameplayBuffBase::Merge (UGameplayBuffBase* InBuff, const FGameplayBuffParams& Params) { if (Owner == nullptr ) return ; OnMerge (InBuff, Params); } void UGameplayBuffBase::Tick (float DeltaTime) { OnTick (); } void UGameplayBuffBase::CombineParams (const FGameplayBuffParams& Params) { BuffParams = BuffParams + Params; }
TimeDurationBuff
Time时间流逝的 Buff
,需要支持 Refresh
、 Pause
、UpdateSpeedFactor
等操作;
基础的 TimeDurationBuff
的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 void UGameplayBuff_TimeDuration::OnCreate () { Super::OnCreate (); TimeDuration = BaseTimeDuration; float InDuration = BuffParams.GetValue ("Duration" ); if (InDuration != 0 ) TimeDuration = InDuration; PassDuration = 0.0f ; LastTickTime = GetWorldTimeNow (); } void UGameplayBuff_TimeDuration::Tick (float DeltaTime) { if (GetWorld () == nullptr || GetWorld ()->GetGameState () == nullptr ) return ; float CurrentTickTime = GetWorldTimeNow (); TickInternal = CurrentTickTime - LastTickTime; LastTickTime = CurrentTickTime; if (CheckInPause () == false && CheckNeedToStop () == false ) { PassDuration += TickInternal * SpeedFactor; OnTick (); } }
Refresh
:刷新倒计时时间:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 void UGameplayBuff_TimeDuration::Refresh (float InPassDuration, float InTimeDuration) { if (InPassDuration >= 0 ) { PassDuration = InPassDuration; } if (InTimeDuration >= 0 ) { TimeDuration = InTimeDuration; } OnRefresh (); }
Pause
:根据不同的 Reason
暂停/重启 时间:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 void UGameplayBuff_TimeDuration::BeginPauseTime (FString Reason) { if (Owner == nullptr ) return ; PauseReasons.AddUnique (Reason); if (bInPause == true ) return ; if (PauseReasons.Num () <= 0 ) return ; bInPause = true ; OnBeginPauseTime (); } void UGameplayBuff_TimeDuration::StopPauseTime (FString Reason) { if (Owner == nullptr ) return ; PauseReasons.Remove (Reason); if (bInPause == false ) return ; if (PauseReasons.Num () > 0 ) return ; bInPause = false ; OnStopPauseTime (); }
UpdateSpeedFactor
: 更新 Buff
的速度
1 2 3 4 5 6 7 void UGameplayBuff_TimeDuration::UpdateSpeedFactor (float InFactor) { if (Owner == nullptr ) return ; if (SpeedFactor == InFactor) return ; SpeedFactor = InFactor; OnUpdateSpeedFactor (); }
BuffUtils
暴露给外部系统使用的 Utils
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 #pragma region Base public : UFUNCTION () static UGameplayBuffManager* GetBuffSystem (UWorld* World = nullptr ) ; UFUNCTION () static UGameplayBuffComponent* GetBuffComponent (APlayerState* PS) ; #pragma endregion Base #pragma region Setting public : UFUNCTION () static int GetBuffID ( FString BuffName ) ; UFUNCTION () static FString GetBuffName ( int BuffID ) ; UFUNCTION () static float GetBuffParam (FString BuffName, FString ParamName) ; static float GetBuffParam (int BuffID, FString ParamName) ; static TArray<int > GetBuffIDsByTag (FString Tag) ; #pragma endregion Setting #pragma region Get public : UFUNCTION () static TArray<UGameplayBuffBase*> GetGameplayBuff (APlayerState* PS, FString BuffName) ; static TArray<UGameplayBuffBase*> GetGameplayBuff (APlayerState* PS, int ID) ; #pragma endregion Get #pragma region Add public : UFUNCTION (BlueprintCallable) static UGameplayBuffBase* AddGameplayBuff (APlayerState* PS, FString BuffName) ; static UGameplayBuffBase* AddGameplayBuff (APlayerState* PS, int BuffID) ; static UGameplayBuffBase* AddGameplayBuff (APlayerState* PS, FString BuffName, const FGameplayBuffParams& Params) ; static UGameplayBuffBase* AddGameplayBuff (APlayerState* PS, int BuffID, const FGameplayBuffParams& Params) ; #pragma endregion Add #pragma region Remove public : UFUNCTION (BlueprintCallable) static void RemoveGameplayBuff (APlayerState* PS, FString BuffName, bool bRemoveImediately = false ) ; static void RemoveGameplayBuff (APlayerState* PS, FString BuffName, bool bRemoveImediately, const FGameplayBuffParams& Params) ; static void RemoveGameplayBuff (APlayerState* PS, int ID, bool bRemoveImediately = false , const FGameplayBuffParams& Params = {}) ; static void RemoveGameplayBuff (APlayerState* PS, UGameplayBuffBase* Buff, bool bRemoveImediately = false , const FGameplayBuffParams& Params = {}) ; static void RemoveGameplayBuffsByTag (APlayerState* PS, FString Tag, bool bRemoveImediately = false , const FGameplayBuffParams& Params = {}) ; static void ClearGameplayBuff (APlayerState* PS, const FGameplayBuffParams& Params = {}) ; #pragma endregion Remove #pragma region CombineParams public : UFUNCTION () static void CombineGameplayBuffParams (APlayerState* PS, FString BuffName, const FGameplayBuffParams& Params = {}) ; static void CombineGameplayBuffParams (APlayerState* PS, int ID, const FGameplayBuffParams& Params = {}) ; static void CombineGameplayBuffParams (UGameplayBuffBase* Buff, const FGameplayBuffParams& Params = {}) ; static void CombineGameplayBuffParams (APlayerState* PS, const FGameplayBuffParams& Params = {}) ; #pragma endregion CombineParams #pragma region CheckExist public : UFUNCTION () static bool CheckGameplayBuffExist (APlayerState* PS, FString BuffName) ; static bool CheckGameplayBuffExist (APlayerState* PS, int BuffID) ; static bool CheckGameplayBuffHandleExist (APlayerState* PS, UGameplayBuffBase* Buff) ; #pragma endregion CheckExist
其它信息
GameplayBuffParams
Buff
的 Params
部分,
继承 FCommonVariantParams ,实现一个 ValueMap
记录各种类型的参数,同时额外记录 Tags
;
这样使用的时候就可以这样使用 Params
:
1 2 3 4 5 6 7 UGameplayBuffUtils::AddGameplayBuff (Target, BuffID, { {"ParamA" , (float )A}, {"ParamsB" , (int )B } }); float A = BuffParams.GetValue<float >("ParamA" );int B = BuffParams.GetValue<int >("ParamB" );
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 USTRUCT ()struct FGameplayBuffParams : public FCommonVariantParams{ GENERATED_BODY () FGameplayBuffParams () = default ; FGameplayBuffParams (const FString& Key, FVariant Value, const TArray<FString>& Tags = {}) : FCommonVariantParams (Key, Value), Tags (Tags) {} FGameplayBuffParams (std::initializer_list<TPairInitializer<const FString&, FVariant>> ValuePairs, const TArray<FString>& InTags = {}) : FCommonVariantParams (ValuePairs), Tags (InTags) {} FGameplayBuffParams (const TArray<FString>& Tags) : Tags (Tags) {} FString ToString () const ; bool NetSerialize (FArchive& Ar, class UPackageMap* Map, bool & bOutSuccess) ; FGameplayBuffParams& operator =(const FCommonVariantParams& OtherParams); FGameplayBuffParams operator +(const FGameplayBuffParams& OtherParams); void Merge (const FGameplayBuffParams& OtherParams) ; void Clear () ; public : UPROPERTY () TArray <FString> Tags; }; template <>struct TStructOpsTypeTraits < FGameplayBuffParams> : TStructOpsTypeTraitsBase2<FGameplayBuffParams>{ enum { WithNetSerializer = true , }; };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 bool FGameplayBuffParams::NetSerialize (FArchive& Ar, UPackageMap* Map, bool & bOutSuccess) { bOutSuccess = true ; Ar << ValueMap; Ar << Tags; return true ; } FGameplayBuffParams& FGameplayBuffParams::operator =(const FCommonVariantParams& OtherParams) { ValueMap = OtherParams.GetValueMap (); Tags = {}; return *this ; } FGameplayBuffParams FGameplayBuffParams::operator +(const FGameplayBuffParams& OtherParams) { FGameplayBuffParams CombinedParams = *this ; CombinedParams.Merge (OtherParams); return CombinedParams; } void FGameplayBuffParams::Merge (const FGameplayBuffParams& OtherParams) { FCommonVariantParams::Merge (OtherParams); for (const auto & Tag : OtherParams.Tags) { Tags.AddUnique ( Tag ); } } void FGameplayBuffParams::Clear () { FCommonVariantParams::Clear (); Tags.Empty (); }
GameplayCountDownData
提供给 UGameplayBuff_TimeDuration
使用,一份记录倒计时的数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 USTRUCT ()struct FGameplayCountDownData { GENERATED_USTRUCT_BODY () FGameplayCountDownData () = default ; FGameplayCountDownData (float StartTime, float TotalTime = 0.0f , float LeftTime = 0.0f , bool IsPause = false , float SpeedFactor = 1.0f ) : StartTime (StartTime), TotalTime (TotalTime), LeftTime (LeftTime), IsPause (IsPause), SpeedFactor (SpeedFactor) {} bool NetSerialize (FArchive& Ar, class UPackageMap* Map, bool & bOutSuccess) { Ar << StartTime; Ar << TotalTime; Ar << LeftTime; Ar << IsPause; Ar << SpeedFactor; bOutSuccess = true ; return true ; } bool operator ==(const FGameplayCountDownData& Other) const { if (StartTime != Other.StartTime) return false ; if (TotalTime != Other.TotalTime) return false ; if (LeftTime != Other.LeftTime) return false ; if (IsPause != Other.IsPause) return false ; if (SpeedFactor != Other.SpeedFactor) return false ; return true ; } bool operator !=(const FGameplayCountDownData& Other) const { return !(*this == Other); } FString ToString () const ; UPROPERTY () float StartTime = 0 ; UPROPERTY () float TotalTime = 0 ; UPROPERTY () float LeftTime = 0 ; UPROPERTY () bool IsPause = false ; UPROPERTY () float SpeedFactor = 1.0 ; };
TODO
Buff
本身的同步:目前 BuffSystem
只同步了一些 Info
(比如 BuffID
等)信息到 Client
,UE5
提供了完整的 ReplicateSubobject
解决方案,可以通过 AddReplicatedSubObject
将一个 UObject
以 BuffComponent
的SubObject
的方式同步下去;UE4
的 ReplicateSubobject
会将所有的 Subobj
由 ActorChanel->CreatSubObjects
持有,并且不能方便地 Remove
;